/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package com.plechinger.mcremote.server;

import com.plechinger.mcremote.data.Request;
import com.plechinger.mcremote.data.RequestMethod;
import com.plechinger.mcremote.data.Response;
import com.plechinger.mcremote.data.ResponseStatus;
import com.plechinger.mcremote.server.exceptions.NoSuitableStrategy;
import com.plechinger.mcremote.server.exceptions.RequestParseException;
import com.plechinger.mcremote.server.strategy.ServerStrategy;
import com.plechinger.mcremote.server.strategy.Strategy;
import java.io.IOException;
import java.util.EnumMap;
import java.util.Map;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.reflections.Reflections;
import org.reflections.util.ClasspathHelper;
import org.reflections.util.ConfigurationBuilder;

/**
 *
 * @author Lukas
 */
public class Dispatcher extends BasicServer {

    private static final Logger LOG = Logger.getLogger(Dispatcher.class.getName());
    private Map<RequestMethod, ServerStrategy> actions = new EnumMap<RequestMethod, ServerStrategy>(RequestMethod.class);

    public Dispatcher(int port) throws IOException {
        super(port);
        LOG.log(Level.FINER, "Load Server Strategies...");
        loadStrategies();
        LOG.log(Level.FINER, "Strategies loaded");
    }

    @Override
    protected Object processRequest(Object req) {
        Response response = new Response();
        try {
            if (req instanceof Request) {
                Request request = (Request) req;

                if (!request.getPassword().equals(Server.getServer().getProperties().getProperty("server.password"))) {
                    throw new Exception("Wrong Password!");
                }

                if (actions.containsKey(request.getMethod())) {
                    LOG.info("Process Request with Method " + request.getMethod());
                    ServerStrategy strategy = actions.get(request.getMethod());
                    response = strategy.process(request);
                    response.setMethod(request.getMethod());
                    return response;
                }
                throw new NoSuitableStrategy("No Strategy for Method " + request.getMethod().name());
            } else {
                throw new RequestParseException("Request could not be parsed.");
            }
        } catch (Exception e) {
            response.setStatus(ResponseStatus.ERROR);
            response.setMessage("Exception occured!");
            e.printStackTrace();
            response.setResponseObject(e);
        }
        return response;
    }

    private void loadStrategies() {
        Reflections reflections = new Reflections(new ConfigurationBuilder().addUrls(ClasspathHelper.forJavaClassPath()));

        Set<Class<? extends ServerStrategy>> strategies = reflections.getSubTypesOf(ServerStrategy.class);

        for (Class<? extends ServerStrategy> strategyClass : strategies) {
            if (strategyClass.isAnnotationPresent(Strategy.class)) {
                try {
                    Strategy annotation = strategyClass.getAnnotation(Strategy.class);
                    ServerStrategy serverStrategy = strategyClass.newInstance();
                    LOG.log(Level.FINER, "Found Strategy for " + annotation.method() + ": " + serverStrategy.getClass().getName());
                    actions.put(annotation.method(), serverStrategy);
                } catch (InstantiationException ex) {
                    LOG.log(Level.SEVERE, null, ex);
                } catch (IllegalAccessException ex) {
                    LOG.log(Level.SEVERE, null, ex);
                }
            }
        }
    }
}
